//*************************************************************************
//                                 全局变量
//*************************************************************************

.text
.global rt_hw_interrupt_disable, rt_hw_interrupt_enable, rt_hw_context_switch_to, rt_hw_context_switch, PendSV_Handler
		
//*************************************************************************
//                                 常量
//*************************************************************************
//-------------------------------------------------------------------------
//使用63号中断进行上下文切换，暂时称为PENDSV
//-------------------------------------------------------------------------
NVIC_PENDSV_CTRL:
    .word   0xC0003018     // PendSV 软件中断设置寄存器(VICSOFTINT)
NVIC_PENDSVPRIADDR:
    .word   0xC000327C     // PendSV 优先级地址(VICVECTPRIORITY31)
NVIC_PENDSV_PRI:
    .word   0x0000000F     // PendSV 优先级值 (lowest)
NVIC_PENDSVSET:
    .word   0x80000000     // 软件触发触发PendSV exception
NVIC_VICSOFTINTCLEAR:
    .word   0xC000301C     // PendSV 软件中断清除寄存器(VICSOFTINTCLEAR)   
NVIC_PENDSVRESET:
    .word   0x80000000     // PendSV exception标志位置0
	
//*************************************************************************
//                              代码产生指令
//*************************************************************************

///*
// * rt_base_t rt_hw_interrupt_disable()//
// */
@ .type rt_hw_interrupt_disable, @function
rt_hw_interrupt_disable:
    MRS     r0, CPSR
    CPSID   I
    BX      lr      

///*
// * void rt_hw_interrupt_enable(rt_base_t level)//
// */
@ .type rt_hw_interrupt_enable, @function
rt_hw_interrupt_enable:
    MSR     CPSR, r0
    BX       lr
		

///*
// *-----------------------------------------------------------------------
// * 函数原型：void rt_hw_context_switch_to(rt_uint32 to)//
// * r0 --> to
// * 该函数用于开启第一次线程切换
// *-----------------------------------------------------------------------
// */
@ .type rt_hw_context_switch_to, @function
rt_hw_context_switch_to:	
    @ bl      log_thread
    // 设置rt_interrupt_to_thread的值
    LDR     r1, =rt_interrupt_to_thread             //将rt_interrupt_to_thread的地址加载到r1
    STR     r0, [r1]                                //将r0的值存储到rt_interrupt_to_thread
    @ bl      log_r0

    // 设置rt_interrupt_from_thread的值为0，表示启动第一次线程切换
    LDR     r1, =rt_interrupt_from_thread           //将rt_interrupt_from_thread的地址加载到r1
    MOV     r0, #0x0                                //配置r0等于0
    STR     r0, [r1]                                //将r0的值存储到rt_interrupt_from_thread

    // 设置中断标志位rt_thread_switch_interrupt_flag的值为1
    LDR     r1, =rt_thread_switch_interrupt_flag    //将rt_thread_switch_interrupt_flag的地址加载到r1
    MOV     r0, #1                                  //配置r0等于1
    STR     r0, [r1]                                //将r0的值存储到rt_thread_switch_interrupt_flag

    // 设置 PendSV 异常的优先级
    LDR     r0, =NVIC_PENDSVPRIADDR
    LDR     r0, [r0]
    LDR     r1, =NVIC_PENDSV_PRI
    LDR     r1, [r1]
    STR     r1, [r0]             // 写

    // 触发 PendSV 异常 (产生上下文切换)
    LDR     r0, =NVIC_PENDSV_CTRL
    LDR     r0, [r0]
    LDR     r1, =NVIC_PENDSVSET
    LDR     r1, [r1]
    STR     r1, [r0]

    bl      my_log

    // 开中断
    CPSIE   F
    CPSIE   I

    // 永远不会到达这里



///*
// *-----------------------------------------------------------------------
// * void rt_hw_context_switch(rt_uint32 from, rt_uint32 to)//
// * r0 --> from
// * r1 --> to
// *-----------------------------------------------------------------------
// */

@ .type rt_hw_context_switch, @function
rt_hw_context_switch:
    @ bl      log_thread
    // 设置中断标志位rt_thread_switch_interrupt_flag为1     
    LDR     r2, =rt_thread_switch_interrupt_flag          // 加载rt_thread_switch_interrupt_flag的地址到r2
    LDR     r3, [r2]                                      // 加载rt_thread_switch_interrupt_flag的值到r3
    CMP     r3, #1                                        // r3与1比较，相等则执行BEQ指令，否则不执行
    BEQ     _reswitch
    MOV     r3, #1                                        // 设置r3的值为1
    STR     r3, [r2]                                      // 将r3的值存储到rt_thread_switch_interrupt_flag，即置1
    // 个人理解，如果rt_thread_switch_interrupt_flag == 1，
    // 说明第一次线程切换没有执行，就不用保存rt_interrupt_from_thread的值
    
	// 设置rt_interrupt_from_thread的值
    LDR     r2, =rt_interrupt_from_thread                 // 加载rt_interrupt_from_thread的地址到r2
    STR     r0, [r2]                                      // 存储r0的值到rt_interrupt_from_thread，即上一个线程栈指针sp的指针

_reswitch:
    // 设置rt_interrupt_to_thread的值
	LDR     r2, =rt_interrupt_to_thread                   // 加载rt_interrupt_to_thread的地址到r2
    STR     r1, [r2]                                      // 存储r1的值到rt_interrupt_to_thread，即下一个线程栈指针sp的指针

    // 触发PendSV异常，实现上下文切换
	LDR     r0, =NVIC_PENDSV_CTRL
    LDR     r0, [r0]
    LDR     r1, =NVIC_PENDSVSET
    LDR     r1, [r1]
    STR     r1, [r0]
	
    // 子程序返回
	BX      lr
	
	// 子程序结束


///*
// *-----------------------------------------------------------------------
// * void PendSV_Handler(void)//
// * r0 --> switch from thread stack
// * r1 --> switch to thread stack
// * psr, pc, lr, r12, r3, r2, r1, r0 are pushed into [from] stack
// *-----------------------------------------------------------------------
// */

PendSV_Handler:
    /* 进_irq后，保存了CPSR，PC，LR，R12，R3-R0 */

    // 获取中断标志位，看看是否为0
    LDR     r0, =rt_thread_switch_interrupt_flag     // 加载rt_thread_switch_interrupt_flag的地址到r0
    LDR     r1, [r0]                                 // 加载rt_thread_switch_interrupt_flag的值到r1
    @ bl      log
    CMP     r1, #0
    BEQ     pendsv_exit                          // 判断r1是否为0，为0则跳转到pendsv_exit
    
    // r1不为0则清0
    MOV     r1, #0x00
    STR     r1, [r0]                                 // 将r1的值存储到rt_thread_switch_interrupt_flag，即清0

    // 判断rt_interrupt_from_thread的值是否为0
    LDR     r0, =rt_interrupt_from_thread            // 加载rt_interrupt_from_thread的地址到r0
    LDR     r1, [r0]                                 // 加载rt_interrupt_from_thread的值到r1

    CMP     r1, #0
    BEQ     switch_to_thread                        // 判断r1是否为0，为0则跳转到switch_to_thread
                                                    // 第一次线程切换时rt_interrupt_from_thread肯定为0，则跳转到switch_to_thread

@ // ========================== 上文保存 ==============================
@     // 当进入PendSVC Handler时，上一个线程运行的环境即：
@     // xPSR，PC（线程入口地址），R14，R12，R3，R2，R1，R0（线程的形参）
@  	// 这些CPU寄存器的值会自动保存到线程的栈中，剩下的r4~r11需要手动保存
 	
@     @ MRS     r1, psp                                  // 获取线程栈指针到r1

    MOV     r1, sp                                  // 获取线程栈指针到r1

    STMFD   r1!, {r4 - r11}                          // 将CPU寄存器r4~r11的值存储到r1指向的地址(每操作一次地址将递减一次)
    LDR     r0, [r0]                                 // 加载r0指向值到r0，即r0=rt_interrupt_from_thread
    STR     r1, [r0]                                 // 将r1的值存储到r0，即更新线程栈sp

    
// ========================== 下文切换 ==============================
switch_to_thread:
    LDR     r1, =rt_interrupt_to_thread               // 加载rt_interrupt_to_thread的地址到r1
                                                      // rt_interrupt_to_thread是一个全局变量，里面存的是线程栈指针SP的指针
    LDR     r1, [r1]                                  // 加载rt_interrupt_to_thread的值到r1，即sp指针的指针
    LDR     r1, [r1]                                  // 加载rt_interrupt_to_thread的值到r1，即sp

    LDMFD   r1!, {r4 - r11}                           //将线程栈指针r1(操作之前先递减)指向的内容加载到CPU寄存器r4~r11

    @ MSR     psp, r1                                   //将线程栈指针更新到PSP
    MOV     sp, r1

pendsv_exit:
    LDR     r0, =NVIC_VICSOFTINTCLEAR                 // 软件触发标志清0 
    LDR     r0, [r0]
    LDR     r1, =NVIC_PENDSVRESET
    LDR     r1, [r1]
    STR     r1, [r0]

    BX      lr                                        // 异常返回
	                                                  // 同时PSP的值也将更新，即指向任务堆栈的栈顶。在ARMC3中，堆是由高地址向低地址生长的。
    // PendSV_Handler 子程序结束	
	
	.ALIGN   4
    